﻿using LiteNetLib;
using LiteNetLib.Utils;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using UnityEngine;

namespace BToolkit
{
    public class NetClient : MonoBehaviour, INetEventListener
    {
        public event Action<ProtoBase> onReceiveOnSubThread, onReceiveOnMainThread;
        public event Action<bool> onDisconnectOnSubThread, onDisconnectOnMainThread;
        public bool isConnected { get { return client != null && client.IsRunning; } }
        public int peerId { get; private set; }

        private NetClient() { }
        private NetManager client;
        private NetDataWriter writer;
        private Action<bool> onConnCallback;
        private bool hasConnCallback;
        private bool isDisconnectByPlayer;
        private bool isRun;
        private Queue<Action> mainThreadActions = new Queue<Action>();

        public static NetClient Create(string tag = null)
        {
            GameObject go = new GameObject(tag != null ? $"NetClient_{tag}" : "NetClient");
            DontDestroyOnLoad(go);
            return go.AddComponent<NetClient>();
        }

        /// <summary>
        /// 连接成功返回PeerId
        /// </summary>
        public void Connect(string ip, int port, string key, Action<bool> callback)
        {
            if (client == null)
            {
                this.onConnCallback = callback;
                hasConnCallback = false;
                client = new NetManager(this);
                client.BroadcastReceiveEnabled = false;
                client.NatPunchEnabled = true;
                client.UpdateTime = 15;
                client.Start();
                client.Connect(ip, port, key);
                writer = new NetDataWriter();
                Task.Run(async () =>
                {
                    isRun = true;
                    while (isRun)
                    {
                        client.PollEvents();
                        await Task.Delay(15);
                    }
                });
            }
        }

        private void Update()
        {
            lock (mainThreadActions)
            {
                while (mainThreadActions.Count > 0)
                {
                    mainThreadActions.Dequeue().Invoke();
                }
            }
        }

        public void OnConnectionRequest(ConnectionRequest request)
        {
        }

        public void OnNetworkError(IPEndPoint endPoint, SocketError socketError)
        {
            Debug.LogError($"[NetClient] {socketError}");
            TextDebug.instance?.Log($"[NetClient] {socketError}");
        }

        public void OnNetworkLatencyUpdate(NetPeer peer, int latency)
        {
        }

        public void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod)
        {
            string msg = reader.GetString();
            Debug.Log($"[NetClient] OnNetworkReceive 收到消息:{msg}");
            ProtoBase proto = LitJson.JsonMapper.ToObject<ProtoBase>(msg);

            if (!hasConnCallback && proto.id == ProtoID.NewConnection)
            {
                peerId = proto.fromPeerId;
                Debug.Log($"[NetClient] 连接成功:peerId:{peerId}");
                isDisconnectByPlayer = false;
                lock (mainThreadActions)
                {
                    mainThreadActions.Enqueue(() =>
                    {
                        onConnCallback?.Invoke(true);
                        onConnCallback = null;
                    });
                }
                hasConnCallback = true;
            }

            onReceiveOnSubThread?.Invoke(proto);
            lock (mainThreadActions)
            {
                mainThreadActions.Enqueue(() =>
                {
                    onReceiveOnMainThread?.Invoke(proto);
                });
            }

            reader.Recycle();
        }

        public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType)
        {
        }

        public void OnPeerConnected(NetPeer peer)
        {
            //这里取peerId不靠谱，经测试，第二个人连依然是0，而此时服务器打印是1
        }

        public void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo)
        {
            onDisconnectOnSubThread?.Invoke(isDisconnectByPlayer);
            lock (mainThreadActions)
            {
                mainThreadActions.Enqueue(() =>
                {
                    onDisconnectOnMainThread?.Invoke(isDisconnectByPlayer);
                });
            }
            Debug.LogError($"[NetClient] 连接已断开:peerId:{peer.Id} {disconnectInfo}");
            TextDebug.instance?.Log($"[NetClient] 连接已断开:peerId:{peer.Id} {disconnectInfo}");
        }

        public void Send(ProtoBase proto)
        {
            string msg = LitJson.JsonMapper.ToJson(proto);
            Debug.Log($"[NetClient]发送消息：{msg}");
            writer.Reset();
            writer.Put(msg);
            client.FirstPeer.Send(writer, DeliveryMethod.ReliableOrdered);
        }

        public void Disconnect()
        {
            isRun = false;
            isDisconnectByPlayer = true;
            onReceiveOnMainThread = null;
            onReceiveOnSubThread = null;
            onDisconnectOnMainThread = null;
            onDisconnectOnSubThread = null;
            client?.Stop();
            client = null;
        }

        private void OnDestroy()
        {
            Disconnect();
        }
    }
}